"""
The simplex of lengths.

    Definition of the simplex of lengths which should be able to answer to
    metaphysical question like :
       is b less than a
       is t less than r
    Of course you can educate the simplex for good answer. But, It's not sure
    that there is no contradiction if you are a bad teacher.
"""

class NoComparison(Exception):
    """
    The exception for missing equation.
    """
    pass


class SimplexOfLengths(object):
    """
    This class is intended to interface at two levels the management of lengths.

        First level: answering
            what are the smallest length in a list
            (at a lower level) comparison of two length

        Second level: education
            add constraint to the polyhedron
        

    EXAMPLES:
        sage : s = Simplex(3)
        sage : k1, k2, k3 = s.generators()
        sage : k1 < k2
        ComparaisonError
        sage : s.constraint("k1 < k2")
        sage : k1 < k2
        True
    """
    def __init__(self, dimension):
        """
        Constructor for the simplex.

            This constructor initializes the element_class which is just a
            vector of value element.
        """

        class Element(object):
            """
            The class of the element of the simplex (depending on the parent
            class). 
            """
            def __init__(self, value, parent):
                """
                Constructor

                    parent : the corresponding Simplex
                    value : the value for creation - coordinate vector, first coordinate is the constant, then the basis of unknowns follows
                """
                
                if not isinstance(value,list):
                    raise ValueError, "Value must be a list of coordinates."
                
                
                self._parent = parent
                self._value = value # no copy???

            def __cmp__(self, other):
                """
                Comparison self - other

                    This return :
                    negative value if other > self
                    zero if other == self
                    positive value if other < self 
                
                TOFIX:
                    Verification must be done in the Python documentation.
                """
                
                if len(self._parent._list_of_constraints_G) > 0 or len(other._parent._list_of_constraints_G) > 0:
                    raise ValueError, "Constraints contain a strict inequality - not implemented yet"    
                
                polyh1 = ieq_to_vert(self._parent._list_of_constraints_GE,self._parent._list_of_constraints_E);
                
                polyh2 = ieq_to_vert(other._parent._list_of_constraints_GE,other._parent._list_of_constraints_E);
                
               
                
                
                
                raise NoComparison

            def __add__(self, other):
                """
                Addition
                """               
                return self._parent._element_class([self[i] + other[i] for i in range(self._parent._dimension + 1)],self._parent)

            def __sub__(self, other):
                """
                Subtraction
                """
                return self._parent._element_class([self[i] - other[i] for i in range(self._parent._dimension + 1)],self._parent)

            def __mul__(self, other):
                """
                Multiplication
                """
                if type(other) == type(0):
                    return self._parent._element_class([self[i]*other for i in range(self._parent._dimension + 1)],self._parent)
                
                raise NotImplemented
            
            def __imul__(self, other):
                """
                Multiplication *=
                """
                if type(other) == type(0):
                    return self._parent._element_class([self[i]*other for i in range(self._parent._dimension + 1)],self._parent)
                
                raise NotImplemented
            
            def __neg__(self):
                """
                Negation
                """
                return self._parent._element_class([-self[i] for i in range(self._parent._dimension + 1)],self._parent)
 

            # existensial question, what is called on -x ?
            
            def __str__(self):
                return str(self._value)
            
            def __getitem__(self, k):
                return self._value[k]

        self._dimension = dimension  # the number of unknowns
        self._element_class = Element  # the class of the element
        
        #self._list_of_inequality = [] # the list of stored inequality
        
        self._list_of_constraints_GE = []
        self._list_of_constraints_G = []
        self._list_of_constraints_E = []
               
    
    def __generate_generators(self):
        i = 0
        
        while i < self._dimension:
            i += 1
            v = [0]*(self._dimension+1)
            v[i] = 1            
            yield self._element_class(v,self)
             
    
    def generators(self):
        """
        Return the tuple of generators.
            
            For the special case of Simplex(0) this return None.
        """
        if self._dimension == 0:
            return None
        
        return list(self.__generate_generators())

    def __call__(self, data):
        """
        Return an element of this simplex depending on the data.

            It's not very intersting to use this because the class provides a
            natural coercion mecanism inside the arithmetic operations methods. But
            this one is more souple.

        EXAMPLES:
            sage : s = Simplex(4)
            sage : k1, k2, k3, k4 = s.generators()
            sage : one = s(1)
            sage : one in s
            True
        """
        if isinstance(data, self._element_class):
            return data
        elif isinstance(data,list):
            return self._element_class(data,parent)
        else:
            raise ValueError, "Not possible to create an element with this data"

    def is_empty(self):
        """
        Tests if the simplex has a solution.

            Just check that there is no contradiction in the list of stored
            inequality.

        EXAMPLES:
            sage : s = Simplex(-1)
            sage : S.is_empty()
            True
            sage : s = Simplex(2)
            sage : s.constraint("k1 < k2")
            sage : s.is_empty()
            False
            sage : s.constraint("k2 < k1")
            sage : s.is_empty()
            True
        """
        return True
    
    def one_element(self):
        """
        Returns an element if the simplex is non empty.
        """
        return None

    def simplify(self):
        """
        Try to simplify the list of inequality.

            Good luck.
        """
        pass
    
    def min(self, list_of_elements):
        """
        Return the minimum value of the list.

            raise NoComparison if the problem is not decidable with the actual
            list of inequalitites.
        """
        if list_of_elements == []:
            raise ValueError, "Your list is empty"

        minimum = self(list_of_elements[0])
        for i in list_of_element:
            tmp = self(i)
            if tmp < minimum:
                minimum = tmp
        return minimum

    def constraint(self, constraint, type):
        """
        Add an inequality or an equality to the simplex.
        
        The format is [const,g1,g2,...] - coordinates in the current basis.

            raise a Value Error if it's not possible to add this constraint.
        """
        
        if not isinstance(constraint,list):
            raise ValueError, "Constraint must be a list of coordinates in the basis [c,g1,g2,...]"
        
        if len(constraint) != self._dimension+1:
            raise ValueError, "Constraint has wrong dimension / number of unknowns - it has to be a vector of length" + (self._dimension+1) + "."
        
        if type == '<':
            self._list_of_constraints_G.append(-1*constraint)
        elif type == '>':
            self._list_of_constraints_G.append(constraint)
        elif type == '<=':
            self._list_of_constraints_GE.append(-1*constraint)
        elif type == '>=':
            self._list_of_constraints_GE.append(constraint)
        elif type == '==':
            self._list_of_constraints_E.append(constraint)
        else:
            raise ValueError, "Unknown type " + type
    
 
